home *** CD-ROM | disk | FTP | other *** search
- /* v f p r i n t f
- *
- * This routine provides the same functionality as fprintf
- * except that it uses varargs.h.
- *
- * In this implementation, this routine is basis for all the
- * formatted output routines. printf, fprintf, and sprintf
- * are all defined in terms of vfprintf.
- *
- * The user is referred to the manual page for a full
- * description of the formats available.
- *
- * The function returns the number of bytes generated, or
- * EOF in the case of an error.
- *
- * Patchlevel 1.0
- *
- * Edit History:
- * 03-Sep-1989 Use PUTC() for faster printf processing. Unroll
- * formatting loops.
- */
-
- #include "stdiolib.h"
-
- /*LINTLIBRARY*/
-
- #define MINBITS 3 /* least bits required --- octal */
- #define MAXPREFIX 3 /* overhead for prefix */
-
- #define MAXDIGITS ((sizeof(long)+MINBITS-1)/MINBITS*MINBITS)
- #define VBUFFERSIZE MAXDIGITS+MAXPREFIX
-
- #define OPTIONAL(p,c,f,v) if(*(p)==(c)){(f)=(v);(p)++;}
-
- /* Get an integer argument
- *
- * Scan the string beginning at the point indicated for an
- * integer argument. If a '*' is found, the argument pointer
- * is used to retrieve a remote integer.
- */
-
- static int integer(p, ap)
-
- char **p; /* pointer to string pointer */
- va_list *ap; /* pointer to argument list */
-
- {
- char *cp; /* character pointer */
- int v; /* return value */
-
- cp = *p;
-
- if (*cp == '*') {
- v = va_arg(*ap, int);
- cp++;
- }
- else {
- for (v = 0; *cp >= '0' && *cp <= '9'; ) {
- v *= 10;
- v += (*cp++ - '0');
- }
- }
- *p = cp;
- return v;
- }
-
- int vfprintf(fp, fmt, args)
-
- FILE *fp; /* stream for output */
- char *fmt; /* format for output */
- va_list args; /* argument list */
-
- {
- char buf[VBUFFERSIZE]; /* workspace */
- int bytes; /* bytes output */
-
- char *p, *q; /* pointers into workspace */
-
- char leftjustify; /* left justify */
- char showsign; /* display a sign */
- char blankprefix; /* prefix with blank */
- char alternate; /* alternate format */
-
- char zerofill; /* fill with zeros */
- char flush; /* need to flush afterwards */
- int width; /* field width */
-
- int precision; /* precision */
-
- char longflag; /* number is long */
-
- char capitals; /* capitals */
- char backward; /* direction for scan */
-
- char prefix; /* number has prefix -/ /+ */
-
- unsigned int radix; /* radix for conversion */
- char sign; /* conversion is signed */
- char negative; /* number is negative */
- long vl; /* conversion temporary */
- unsigned short vs; /* conversion temporary */
- char shortint; /* short or long */
- int c; /* conversion temporary */
- int length; /* raw length of output */
-
- for (flush = bytes = 0; *fmt != 0; ) {
-
- /* Look for the next format field */
- for (p = fmt; ; ) {
- if (*p == 0 || *p == '%') break;
- if (PUTC(*p++, fp) == '\n') flush = 1;
- if (*p == 0 || *p == '%') break;
- if (PUTC(*p++, fp) == '\n') flush = 1;
- }
- bytes += p - fmt;
- if (*p == 0) break;
- fmt = p + 1;
-
- /* Initialise conversion variables */
- p = q = buf;
-
- leftjustify = 0;
- showsign = 0;
- blankprefix = 0;
- alternate = 0;
-
- zerofill = ' ';
- width = 0;
-
- precision = -1;
-
- longflag = 0;
- capitals = 0;
-
- backward = 0;
-
- prefix = 0;
-
- /* Check for optional flags */
- for ( ; ; ) {
- switch (*fmt) {
- case '-': leftjustify = 1; break;
- case '+': showsign = 1; break;
- case ' ': blankprefix = 1; break;
- case '#': alternate = 1; break;
- default: goto DoneFlags;
- }
- fmt++;
- }
- DoneFlags:
-
- /* Check for field width */
- OPTIONAL(fmt, '0', zerofill, '0');
- width = integer(&fmt, &args);
-
- /* Check for precision */
- if (*fmt == '.') {
- fmt++;
- precision = integer(&fmt, &args);
- }
-
- /* Check for longs */
- OPTIONAL(fmt, 'l', longflag, 1);
-
- /* Switch through all the format options */
- switch (*fmt) {
-
- case 'X':
- capitals++;
- case 'x':
- radix = 16;
- sign = 0;
- goto oxud;
-
- case 'u':
- radix = 10;
- sign = 0;
- goto oxud;
-
- case 'o':
- radix = 8;
- sign = 0;
- goto oxud;
-
- case 'd':
- radix = 10;
- sign = 1;
-
- oxud:
- backward++;
- vs = shortint = 0;
- if (longflag)
- if (sign) vl = va_arg(args, long);
- else vl = va_arg(args, unsigned long);
- else
- if (sign) vl = va_arg(args, int);
- else vl = va_arg(args, unsigned);
- if ((negative = sign && vl < 0) != 0)
- vl = -vl;
- do {
- Conversion:
- if (shortint) {
- c = (vs % (unsigned short) radix) + '0';
- vs /= (unsigned short) radix;
- }
- else {
- if (vl == (unsigned short) vl) {
- vs = (unsigned short) vl;
- shortint = 1;
- goto Conversion;
- }
- else {
- c = (int) ((unsigned long) (vl) % radix) + '0';
- vl = (unsigned long) (vl) / radix;
- }
- }
- if (c > '9') {
- c += ('a' - '9' - 1);
- if (capitals)
- c+= ('A' - 'a');
- }
- *p++ = c;
- } while (shortint ? (vs != 0) : (vl != 0));
- if (precision > width) {
- width = precision;
- zerofill = '0';
- }
- if (negative) {
- prefix++;
- *p++ = '-';
- }
- else if (sign) {
- if (showsign) {
- prefix++;
- *p++ = '+';
- }
- else if (blankprefix) {
- prefix++;
- *p++ = ' ';
- }
- }
- if (alternate) {
- if (radix == 8) {
- prefix++;
- *p++ = '0';
- }
- else if (radix == 16) {
- prefix += 2;
- *p++ = capitals ? 'X' : 'x';
- *p++ = '0';
- }
- }
- break;
-
- /* Single character format */
- case 'c':
- if ((*p++ = va_arg(args, int)) == '\n')
- flush = 1;
- break;
-
- /* String format */
- case 's':
- if ((q = va_arg(args, char *)) == NULL)
- q = "(null)";
- p = q;
- if (precision > 0) {
- UNROLL_DO(dostring, precision,
- if (*p == 0) break;
- if (*p++ == '\n') flush = 1);
- }
- else if (precision < 0) {
- for ( ; ; ) {
- if (*p == 0) break;
- if (*p++ == '\n') flush = 1;
- }
- }
- break;
-
- /* Default just print it */
- default:
- if ((*p++ = *fmt) == '\n')
- flush = 1;
- break;
- }
-
- /* Scan past conversion character */
- fmt++;
-
- /* Length of string to be printed */
- length = p - q;
-
- /* Subtract to find padding required */
- if ((width -= length) < 0)
- width = 0;
-
- /* Signal left justification */
- if (leftjustify == 0)
- width = -width;
-
- /* Check for left justification */
- if (width < 0) {
-
- /* Check for negative and zero fill */
- if (zerofill == '0') {
- if (prefix != 0) {
- bytes += prefix;
- length -= prefix;
- UNROLL_DO(doprefix, prefix, PUTC(backward ? *--p : *q++, fp));
- }
- }
-
- /* Now output the rest of the padding */
- width = -width;
- bytes += width;
- UNROLL_DO(doleft, width, PUTC(zerofill, fp));
- }
-
- /* Output the string proper */
- if (length > 0) {
- bytes += length;
- UNROLL_DO(dooutput, length, PUTC(backward ? *--p : *q++, fp));
- }
-
- /* Do right padding */
- if (width != 0) {
- bytes += width;
- UNROLL_DO(doright, width, PUTC(' ', fp));
- }
- }
-
- /* Flush line buffered streams */
- if (flush && TESTFLAG(fp, _IOLBF))
- (void) fflush(fp);
-
- return ferror(fp) ? EOF : bytes;
- }
-